package cn.achao.csr.auth.service.imp;

import cn.achao.csr.api.AliLoginApi;
import cn.achao.csr.api.SysUserApi;
import cn.achao.csr.api.model.dto.RegisterDTO;
import cn.achao.csr.api.model.dto.ZfbNotifyDTO;
import cn.achao.csr.api.model.dto.ZfbRegisterDTO;
import cn.achao.csr.api.model.dto.AuthDTO;
import cn.achao.csr.api.model.entity.LoginUser;
import cn.achao.csr.api.model.entity.SysUser;
import cn.achao.csr.api.model.resp.AliLoginResp;
import cn.achao.csr.auth.service.LoginService;
import cn.achao.csr.auth.service.PasswordService;
import cn.achao.csr.base.constant.*;
import cn.achao.csr.base.constant.enums.RegisterTypeEnum;
import cn.achao.csr.base.constant.enums.SysSourceEnum;
import cn.achao.csr.base.constant.enums.ThirdAuthEnum;
import cn.achao.csr.base.exception.BusinessException;
import cn.achao.csr.base.resp.R;
import cn.achao.csr.base.utils.StringUtils;
import cn.achao.csr.base.utils.ThrowUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

/**
 * @Author: achao
 **/
@Slf4j
@Service
@RequiredArgsConstructor
public class LoginServiceImpl implements LoginService {

    private final SysUserApi userApi;
    private final AliLoginApi aliLoginApi;
    private final PasswordService passwordService;


    @Override
    public LoginUser login(String userAccount, String userPwd) {
        // 用户名或密码为空 错误
        ThrowUtils.throwIf(StringUtils.isAnyBlank(userAccount, userPwd),
                BusinessException.fail("账号/密码必须填写", SysSourceEnum.CSR_AUTH.getSysName()));

        // 密码如果不在指定范围内 错误
        ThrowUtils.throwIf(userPwd.length() < UserConstants.PASSWORD_MIN_LENGTH || userPwd.length() > UserConstants.PASSWORD_MAX_LENGTH,
                BusinessException.fail("用户密码不在指定范围", SysSourceEnum.CSR_AUTH.getSysName()));

        // 用户名不在指定范围内 错误
        ThrowUtils.throwIf(userAccount.length() < UserConstants.USERNAME_MIN_LENGTH
                        || userAccount.length() > UserConstants.USERNAME_MAX_LENGTH,
                BusinessException.fail("用户名不在指定范围", SysSourceEnum.CSR_AUTH.getSysName()));

        // TODO IP黑名单校验

        // 查询用户信息
        R<LoginUser> userResult = userApi.info(userAccount, SecurityConstants.INNER);
        ThrowUtils.throwIf(R.isError(userResult),
                BusinessException.fail("查询用户信息失败", SysSourceEnum.CSR_SYSTEM.getSysName()));

        LoginUser userInfo = checkLoginUser(userResult.getData(), userPwd);
        return userInfo;
    }


    @Override
    public AliLoginResp zfbLogin(String status, String userAccount) {
        if (StringUtils.isNotBlank(userAccount)) {
            status = status + userAccount;
        }
        R<AliLoginResp> resp = aliLoginApi.zfbLogin(status, SecurityConstants.INNER);
        ThrowUtils.throwIf(R.FAIL == resp.getCode(),
                BusinessException.fail("支付宝暂不可用", SysSourceEnum.CSR_ZFB.getSysName()));
        return resp.getData();
    }

    @Override
    public LoginUser zfbNotify(ZfbNotifyDTO req) {
        String status = req.getStatus();
        ZfbRegisterDTO registerInfo = req.getRegisterInfo();
        ThrowUtils.throwIf(registerInfo == null || StringUtils.isBlank(registerInfo.getZfbOpenId()),
                BusinessException.fail("支付宝登录失败", SysSourceEnum.CSR_ZFB.getSysName()));
        String openId = registerInfo.getZfbOpenId();
        // 登录
        if (AliConstants.MODEL_LOGIN.equals(status)) {
            R<Boolean> existResp = userApi.isExistOpenId(openId, ThirdAuthEnum.ZFB, SecurityConstants.INNER);
            ThrowUtils.throwIf(R.isError(existResp),
                    BusinessException.fail("支付宝服务异常", SysSourceEnum.CSR_SYSTEM.getSysName()));
            Boolean isExist = existResp.getData();
            if (isExist) {
                // 登录
                R<LoginUser> loginUserR = userApi.infoByZfbOpenId(openId, SecurityConstants.INNER);
                ThrowUtils.throwIf(R.isError(loginUserR),
                        BusinessException.fail("支付宝登录失败", SysSourceEnum.CSR_SYSTEM.getSysName()));
                LoginUser data = loginUserR.getData();
                if (data != null) {
                    return checkLoginUser(data, "");
                }
            } else {
                // 注册
                R<LoginUser> registerUserR = userApi.register(BuildRegisterByZfb(registerInfo));
                ThrowUtils.throwIf(R.isError(registerUserR),
                        BusinessException.fail("支付宝注册失败", SysSourceEnum.CSR_SYSTEM.getSysName()));
                LoginUser data = registerUserR.getData();
                if (data != null) {
                    return checkLoginUser(data, "");
                }
            }
            return null;
        }
        // 授权
        if (AliConstants.MODEL_AUTH.equals(status)) {
            String userAccount = req.getUserAccount();
            String userInfo = registerInfo.getZfbUserInfo();

            R<LoginUser> loginUserR = userApi.updateZfbOpenId(AuthDTO.builder()
                    .openId(openId)
                    .userAccount(userAccount)
                    .userInfo(userInfo)
                    .thirdAuth(ThirdAuthEnum.ZFB)
                    .build(), SecurityConstants.INNER);
            ThrowUtils.throwIf(R.isError(loginUserR),
                    BusinessException.fail("支付宝授权失败", SysSourceEnum.CSR_SYSTEM.getSysName()));
            LoginUser data = loginUserR.getData();
            if (data != null) {
                return checkLoginUser(data, "");
            }
        }
        ThrowUtils.throwIf(BusinessException.fail("支付宝状态码异常", SysSourceEnum.CSR_ZFB.getSysName()));
        return null;
    }


    /**
     * 构建支付宝注册信息
     * @param registerInfo
     * @return
     */
    private RegisterDTO BuildRegisterByZfb(ZfbRegisterDTO registerInfo) {
        return RegisterDTO.builder()
                .registerType(RegisterTypeEnum.ZFB)
                .zfbOpenId(registerInfo.getZfbOpenId())
                .zfbUserInfo(registerInfo.getZfbUserInfo())
                .nickName(registerInfo.getNickName())
                .build();
    }

    /**
     * 校验用户信息
     * @param userPwd
     * @param userInfo
     * @return
     */
    private LoginUser checkLoginUser(LoginUser userInfo, String userPwd) {
        SysUser user = userInfo.getUser();
        // 账号被删除
        ThrowUtils.throwIf(Constants.DEL.equals(user.getIsDel()),
                BusinessException.fail("对不起，您的账号已被删除", SysSourceEnum.CSR_SYSTEM.getSysName()));
        // TODO 账号被禁用

        // 密码校验
        if (StringUtils.isNotBlank(userPwd)) {
            passwordService.validate(user, userPwd);
        }
        return userInfo;
    }
}
